//===--- GenConstant.cpp - Swift IR Generation For Constants --------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
//  This file implements IR generation for constant values.
//
//===----------------------------------------------------------------------===//

#include "llvm/IR/Constants.h"

#include "polarphp/irgen/internal/GenConstant.h"
#include "polarphp/irgen/internal/GenIntegerLiteral.h"
#include "polarphp/irgen/internal/GenStruct.h"
#include "polarphp/irgen/internal/GenTuple.h"
#include "polarphp/irgen/internal/TypeInfo.h"
#include "polarphp/irgen/internal/StructLayout.h"
#include "polarphp/basic/Range.h"
#include "polarphp/pil/lang/PILModule.h"

using namespace polar;
using namespace irgen;

llvm::Constant *irgen::emitConstantInt(IRGenModule &IGM,
                                       IntegerLiteralInst *ILI) {
   BuiltinIntegerWidth width
      = ILI->getType().castTo<AnyBuiltinIntegerType>()->getWidth();

   // Handle arbitrary-precision integers.
   if (width.isArbitraryWidth()) {
      auto pair = emitConstantIntegerLiteral(IGM, ILI);
      auto type = IGM.getIntegerLiteralTy();
      return llvm::ConstantStruct::get(type, { pair.Data, pair.Flags });
   }

   APInt value = ILI->getValue();

   // The value may need truncation if its type had an abstract size.
   if (width.isPointerWidth()) {
      unsigned pointerWidth = IGM.getPointerSize().getValueInBits();
      assert(pointerWidth <= value.getBitWidth()
             && "lost precision at AST/PIL level?!");
      if (pointerWidth < value.getBitWidth())
         value = value.trunc(pointerWidth);
   } else {
      assert(width.isFixedWidth() && "impossible width value");
   }

   return llvm::ConstantInt::get(IGM.LLVMContext, value);
}

llvm::Constant *irgen::emitConstantFP(IRGenModule &IGM, FloatLiteralInst *FLI) {
   return llvm::ConstantFP::get(IGM.LLVMContext, FLI->getValue());
}

llvm::Constant *irgen::emitAddrOfConstantString(IRGenModule &IGM,
                                                StringLiteralInst *SLI) {
   switch (SLI->getEncoding()) {
      case StringLiteralInst::Encoding::Bytes:
      case StringLiteralInst::Encoding::UTF8:
         return IGM.getAddrOfGlobalString(SLI->getValue());

      case StringLiteralInst::Encoding::UTF16: {
         // This is always a GEP of a GlobalVariable with a nul terminator.
         auto addr = IGM.getAddrOfGlobalUTF16String(SLI->getValue());

         // Cast to Builtin.RawPointer.
         return llvm::ConstantExpr::getBitCast(addr, IGM.Int8PtrTy);
      }

      case StringLiteralInst::Encoding::ObjCSelector:
         llvm_unreachable("cannot get the address of an Objective-C selector");
   }
   llvm_unreachable("bad string encoding");
}

static llvm::Constant *emitConstantValue(IRGenModule &IGM, PILValue operand) {
   if (auto *SI = dyn_cast<StructInst>(operand)) {
      return emitConstantStruct(IGM, SI);
   } else if (auto *TI = dyn_cast<TupleInst>(operand)) {
      return emitConstantTuple(IGM, TI);
   } else if (auto *ILI = dyn_cast<IntegerLiteralInst>(operand)) {
      return emitConstantInt(IGM, ILI);
   } else if (auto *FLI = dyn_cast<FloatLiteralInst>(operand)) {
      return emitConstantFP(IGM, FLI);
   } else if (auto *SLI = dyn_cast<StringLiteralInst>(operand)) {
      return emitAddrOfConstantString(IGM, SLI);
   } else if (auto *BI = dyn_cast<BuiltinInst>(operand)) {
      switch (IGM.getPILModule().getBuiltinInfo(BI->getName()).ID) {
         case BuiltinValueKind::PtrToInt: {
            llvm::Constant *ptr = emitConstantValue(IGM, BI->getArguments()[0]);
            return llvm::ConstantExpr::getPtrToInt(ptr, IGM.IntPtrTy);
         }
         case BuiltinValueKind::ZExtOrBitCast: {
            llvm::Constant *value = emitConstantValue(IGM, BI->getArguments()[0]);
            return llvm::ConstantExpr::getZExtOrBitCast(value, IGM.getStorageType(BI->getType()));
         }
         case BuiltinValueKind::StringObjectOr: {
            // It is a requirement that the or'd bits in the left argument are
            // initialized with 0. Therefore the or-operation is equivalent to an
            // addition. We need an addition to generate a valid relocation.
            llvm::Constant *rhs = emitConstantValue(IGM, BI->getArguments()[1]);
            if (auto *TE = dyn_cast<TupleExtractInst>(BI->getArguments()[0])) {
               // Handle StringObjectOr(tuple_extract(usub_with_overflow(x, offset)), bits)
               // This pattern appears in UTF8 String literal construction.
               // Generate the equivalent: add(x, sub(bits - offset)
               BuiltinInst *SubtrBI =
                  PILGlobalVariable::getOffsetSubtract(TE, IGM.getPILModule());
               assert(SubtrBI && "unsupported argument of StringObjectOr");
               auto *ptr = emitConstantValue(IGM, SubtrBI->getArguments()[0]);
               auto *offset = emitConstantValue(IGM, SubtrBI->getArguments()[1]);
               auto *totalOffset = llvm::ConstantExpr::getSub(rhs, offset);
               return llvm::ConstantExpr::getAdd(ptr, totalOffset);
            }
            llvm::Constant *lhs = emitConstantValue(IGM, BI->getArguments()[0]);
            return llvm::ConstantExpr::getAdd(lhs, rhs);
         }
         default:
            llvm_unreachable("unsupported builtin for constant expression");
      }
   } else if (auto *VTBI = dyn_cast<ValueToBridgeObjectInst>(operand)) {
      auto *val = emitConstantValue(IGM, VTBI->getOperand());
      auto *sTy = IGM.getTypeInfo(VTBI->getType()).getStorageType();
      return llvm::ConstantExpr::getIntToPtr(val, sTy);
   } else {
      llvm_unreachable("Unsupported PILInstruction in static initializer!");
   }
}

namespace {

/// Fill in the missing values for padding.
void insertPadding(SmallVectorImpl<llvm::Constant *> &Elements,
                   llvm::StructType *sTy) {
   // fill in any gaps, which are the explicit padding that swiftc inserts.
   for (unsigned i = 0, e = Elements.size(); i != e; i++) {
      auto &elt = Elements[i];
      if (elt == nullptr) {
         auto *eltTy = sTy->getElementType(i);
         assert(eltTy->isArrayTy() &&
                eltTy->getArrayElementType()->isIntegerTy(8) &&
                "Unexpected non-byte-array type for constant struct padding");
         elt = llvm::UndefValue::get(eltTy);
      }
   }
}

template <typename InstTy, typename NextIndexFunc>
llvm::Constant *emitConstantStructOrTuple(IRGenModule &IGM, InstTy inst,
                                          NextIndexFunc nextIndex) {
   auto type = inst->getType();
   auto *sTy = cast<llvm::StructType>(IGM.getTypeInfo(type).getStorageType());

   SmallVector<llvm::Constant *, 32> elts(sTy->getNumElements(), nullptr);

   // run over the Swift initializers, putting them into the struct as
   // appropriate.
   for (unsigned i = 0, e = inst->getElements().size(); i != e; i++) {
      auto operand = inst->getOperand(i);
      Optional<unsigned> index = nextIndex(IGM, type, i);
      if (index.hasValue()) {
         assert(elts[index.getValue()] == nullptr &&
                "Unexpected constant struct field overlap");

         elts[index.getValue()] = emitConstantValue(IGM, operand);
      }
   }
   insertPadding(elts, sTy);
   return llvm::ConstantStruct::get(sTy, elts);
}
} // end anonymous namespace

llvm::Constant *irgen::emitConstantStruct(IRGenModule &IGM, StructInst *SI) {
   // The only way to get a struct's stored properties (which we need to map to
   // their physical/LLVM index) is to iterate over the properties
   // progressively. Fortunately the iteration order matches the order of
   // operands in a StructInst.
   auto StoredProperties = SI->getStructDecl()->getStoredProperties();
   auto Iter = StoredProperties.begin();

   return emitConstantStructOrTuple(
      IGM, SI, [&Iter](IRGenModule &IGM, PILType Type, unsigned _i) mutable {
         (void)_i;
         auto *FD = *Iter++;
         return irgen::getPhysicalStructFieldIndex(IGM, Type, FD);
      });
}

llvm::Constant *irgen::emitConstantTuple(IRGenModule &IGM, TupleInst *TI) {
   return emitConstantStructOrTuple(IGM, TI,
                                    irgen::getPhysicalTupleElementStructIndex);
}

llvm::Constant *irgen::emitConstantObject(IRGenModule &IGM, ObjectInst *OI,
                                          StructLayout *ClassLayout) {
   auto *sTy = cast<llvm::StructType>(ClassLayout->getType());
   SmallVector<llvm::Constant *, 32> elts(sTy->getNumElements(), nullptr);

   unsigned NumElems = OI->getAllElements().size();
   assert(NumElems == ClassLayout->getElements().size());

   // Construct the object init value including tail allocated elements.
   for (unsigned i = 0; i != NumElems; i++) {
      PILValue Val = OI->getAllElements()[i];
      const ElementLayout &EL = ClassLayout->getElements()[i];
      if (!EL.isEmpty()) {
         unsigned EltIdx = EL.getStructIndex();
         assert(EltIdx != 0 && "the first element is the object header");
         elts[EltIdx] = emitConstantValue(IGM, Val);
      }
   }
   // Construct the object header.
   llvm::Type *ObjectHeaderTy = sTy->getElementType(0);
   assert(ObjectHeaderTy->isStructTy());
   elts[0] = llvm::Constant::getNullValue(ObjectHeaderTy);
   insertPadding(elts, sTy);
   return llvm::ConstantStruct::get(sTy, elts);
}
